﻿using System;
using System.Reflection;

using TypeMock;

namespace StructureMap.TypeMockAutoMocker
{
	public class TypeMockFactory
	{
		private const string AaaNamespace = "TypeMock.ArrangeActAssert";
		private static volatile Assembly _aaaAssembly;


		private Type MembersEnumType
		{
			get { return AaaAssembly.GetType(AaaNamespace + ".Members"); }
		}

		private Type IsolateType
		{
			get { return AaaAssembly.GetType(AaaNamespace + ".Isolate"); }
		}

		private object Fake
		{
			get
			{
				var fakeProperty = IsolateType.GetProperty("Fake");
				return fakeProperty.GetGetMethod().Invoke(null, new object[] {});
			}
		}

		private Type FakeType
		{
			get { return Fake.GetType(); }
		}

		public Assembly AaaAssembly
		{
			get
			{
				if (_aaaAssembly==null)
				{
					lock (typeof(TypeMockFactory))
					{
						if (_aaaAssembly == null)
						{
							_aaaAssembly = Assembly.Load("Typemock.ArrangeActAssert");
						}
					}
				}
				return _aaaAssembly;
			}
		}

		private static Mock MockObject(Type type, Constructor constructorMocked, bool strict, params object[] args)
		{
			var mock =  MockManager.MockObject(type, constructorMocked, args);
			mock.Strict = strict;
			return mock;
		}

		public object CreateFakeInstanceThatReturnsDefaultValues(Type type, params object[] args)
		{
			// Isolate.Fake.Instance<type>(Members.ReturnNulls);
			var instanceMethod = FakeType.GetMethod("Instance", new[] {MembersEnumType});
			var instanceGenericMethod = instanceMethod.MakeGenericMethod(type);

			var returnNullsValue = Enum.Parse(MembersEnumType, "ReturnNulls");
			return instanceGenericMethod.Invoke(Fake, new[] {returnNullsValue});
		}

		public object CreateFakeInstanceThatCallsToOriginalImplememtation(Type type, params object[] args)
		{
			var mock = MockObject(type, Constructor.NotMocked, false, args);
			return mock.MockedInstance;
		}
	}
}